"""
    语法错误: 解析错误,可能是学习Python中最容易遇到的错误
    while True print("hello world")
    File "<stdin>", line 1
        while True print('hello world')
                       ^
    SyntaxError: invalid syntax


上述就是在开发中经常遇到的语法错误。

异常: 模块中的方法执行过程中检测到的错误被称为异常。异常不一定导致严重后果

# >>> 10 * (1/0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero

# >>> 4 + spam*3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'spam' is not defined

# >>> '2' + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't convert 'int' object to str implicitly

"""

"""
    处理异常：
    try:
        pass
    except XxxError:
        pass
    
    
    try ... except 语句有一个可选的 else 子句，在使用时必须放在所有的 except 子句后面。对于在 try 子句不引发异常时必须执行的代码来说很有用。
    for arg in sys.argv[1:]:
        try:
            f = open(arg, 'r')
        except OSError:
            print('cannot open', arg)
        else:
            print(arg, 'has', len(f.readlines()), 'lines')
            f.close()
"""

import sys
for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except OSError:
        print('cannot open', arg)
    else:
        print(arg, 'has', len(f.readlines()), 'lines')
        f.close()
# 执行：python 05_错误和异常.py 05_错误和异常.py
# 输出：05_错误和异常.py has 58 lines

print("\n-----------------------------------------------\n")
try:
    raise Exception('spam','eggs')
except Exception as inst:
    print(type(inst))
    print(inst.args)
    print(inst)

    x,y = inst.args
    print(x)
    print(y)

# 输出结果：
# <class 'Exception'>
# ('spam', 'eggs')
# ('spam', 'eggs')
# spam
# eggs

print("\n-----------------------------------------------\n")

"""
    异常处理程序不仅处理try子句中遇到的异常，还处理try子句中调用(即使是间接地)的函数内部发生的异常。例如：
"""
def this_fails():
    x = 1/0

try:
    this_fails()
except ZeroDivisionError as err:
    print('Handling run-time error:', err)

# 输出: Handling run-time error: division by zero

"""
    异常抛出
    raise语句允许程序员强制发生指定的异常，例如：
    >>> raise NameError('HiThere')
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    NameError: 'hiThere'
    
    raise 唯一的参数就是要抛出的异常，这个参数必须是一个异常实例或者是一个异常类(派生自Exception的类)。如果传递的是一个异常类，
    它将通过调用没有参数的构造器来隐式实力化：
    raise ValueError 
    
    
    如果你需要确定是否引发了异常但不打算处理它，则可以使用更简单的raise语句形式重新引发异常
"""
try:
    raise NameError('HiThere')
except NameError:
    print('An exception flew by!')
    #raise
# An exception flew by!
# Traceback (most recent call last):
#   File "/Users/ypykip/codes/python/full-stack-python/document/05_错误和异常.py", line 112, in <module>
#     raise NameError('HiThere')
# NameError: HiThere

'''
    用户自定义异常
    程序可以通过创建新的异常类来命名它们自己的异常。异常通常应该直接或间接地从Exception类派生。
    
'''

class Error(Exception):
    pass

class InputError(Error):

    def __init__(self, expression, message):
        self.expression = expression
        self.message = message

class TransitionError(Error):
    def __init__(self, previous, next, message):
        self.previous = previous
        self.next = next
        self.message = message

"""
    如果存在finally子句，则finally子句将作为try语句结束前的最后一项任务被执行。finally子句不论try语句是否产生了异常都会被执行。
    
    一些更复杂的情况：
    1. 如果在执行try子句期间发生了异常，该异常可由一个except子句进行处理，如果异常没有被except子句所处理，则该异常会在finally子句执行之后被重新引发。
    2. 异常也可能在except或else子句执行期间发生。同样地，该异常会在finally子句执行之后被重新引发。
    3. 如果在执行try语句时遇到一个break,continue或return语句，则finally子句将在执行break，continue，或return语句之前被执行
    4. 如果finally子句中包含一个return语句，则finally子句的return语句将在执行tru子句的return语句之前取代后者被执行。
"""
def aa() -> int:
    try:
        return 1
    except Exception:
        print('An exception flew by!')
    finally:
        return 2


print(aa()) # 输出了2，证明了第四条的结论

"""
    预定义的清理工作
    就是在使用完资源后，要释放资源比如下面
    for line in open('mufile.txt'):
        print(line)
    
    
    # 对比
    with open('mufile.txt') as f:
        for line in f:
            print(line)
    # 执行完语句后，即使在处理行遇到问题，文件f也始终会被关闭，和文件一样，提供预定义清理操作的对象将在其文档中指出这一点。
"""


